<template>
  <div ref="panel" id="sceneNode" class="container">
    <div :style="{overflow : 'hidden' , cursor: panel.canMove ? 'pointer':''}"
         @mousedown.right="mousedownView" @contextmenu.prevent
         @contextmenu="panel.canMove = false">
      <!-- 操作按钮 -->
      <div @mousedown.stop
           class="addNodeClass"
           style="margin-top: 5px;margin-left: 20px;float: left;position: absolute;z-index: 25">

        <!-- 节点拖拽新增 -->
        <el-button draggable="true" v-for="(value,key) of nodeType" :key="key" style="margin-left: 5px" size="small"
                   @mousedown.native="startDragOver($event,key)" :icon="value.icon">{{ value.name }}
        </el-button>
      </div>

      <!-- 节点展示区域 -->
      <div class="panel-body" :style="orgTreeStyle" id="points" @drop="onDrop($event)" @dragover="onDragover($event)">
        <div v-for="(sceneNode,index) in sceneNodeList"
             z-index=26
             class="point" :id="sceneNode.uuid"
             @dblclick="viewSceneNode(sceneNode)"
             :style="{ left: sceneNode.left + 'px', top: sceneNode.top + 'px' ,cursor:'move'}">
          <i :class="nodeType[sceneNode.nodeType].icon"></i>
          <!-- 节点名称 -->
          <span>{{ sceneNode.name }}</span>
          <span style="float: right;margin-left: 10px;">
            <i class="el-icon-document-copy" style="cursor: pointer;color: #909399"
               @click="copySceneNode(sceneNode)"></i>
            <i class="el-icon-delete" style="cursor: pointer;color: #909399"
               @click="removeSceneNode(sceneNode)"></i>
          </span>
          <br/>
          <!-- 节点详情 -->
          <div style="margin-left: 5px;margin-top: 5px;max-width: 200px;word-break: break-all;">
            <div size="mini" effect="plain" type="info" style="margin: 0 2px;color: #909399;font-size: smaller">
              <i class="el-icon-arrow-right"></i>
              节点内容
            </div>
          </div>
          <div style="margin-top: 10px">
            <!-- 节点分支 -->
            <template v-if="sceneNode.branchList">
              <el-tag v-for="branch in sceneNode.branchList" :key="branch.id" size="mini" style="margin-left: 5px"
                      :id="'branch_'+ branch.id">{{ branch.name }}
              </el-tag>
            </template>
            <el-button style="padding: 4px 16px;" icon="el-icon-circle-plus-outline" size="mini" @click="addBatch(sceneNode)"></el-button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  import {jsPlumb} from 'jsplumb'
  import {off, on, uuid} from './components/utils'

  export default {
    name: 'App',
    data() {
      return {
        jsPlumb: null,
        sceneNodeList: [
          {
            name: "入口",
            uuid: "0cbf24d8-7f68-a6d3-857a-e6e09ee3b3b8",
            nodeType: "ENTRANCE",
            top: 286,
            left: 361,
            branchList: [{
              id: '100',
              name: "默认",
              nextNodeUuid: "c2150601-852d-71f2-dd3c-0215112b6d93",
            }]
          }, {
            name: "收集",
            uuid: "c2150601-852d-71f2-dd3c-0215112b6d93",
            nodeType: "COLLECT",
            top: 423,
            left: 407,
            branchList: [{
              id: '101',
              name: "失败",
              nextNodeUuid: "",
            }, {
              id: '102',
              name: "默认",
              nextNodeUuid: "1522f099-fca0-1777-35cc-c962da02d5c4",
            }]
          }, {
            name: "处理",
            uuid: "1522f099-fca0-1777-35cc-c962da02d5c4",
            nodeType: "ACTION",
            top: 570,
            left: 481,
            branchList: [{
              id: '103',
              name: "失败",
              nextNodeUuid: "",
            }, {
              id: '104',
              name: "默认",
              nextNodeUuid: "bb49aef8-8201-037a-d696-c48c7dd953ed",
            }]
          }, {
            name: "响应",
            uuid: "bb49aef8-8201-037a-d696-c48c7dd953ed",
            nodeType: "RESPONSE",
            top: 718,
            left: 531,
            branchList: [{
              id: '105',
              name: "默认",
              nextNodeUuid: "",
            }]
          }],
        sceneNodeDocument: null,
        nodeDrawerModel: false,
        nodeType: {
          ENTRANCE: {
            name: '意图入口',
            icon: 'el-icon-video-play',
            buttonType: 'primary',
            count: 1,
          },
          COLLECT: {
            name: '槽位收集',
            icon: 'el-icon-document-remove',
            buttonType: 'success',
            count: 1,
          },
          ACTION: {
            name: '处理动作',
            icon: 'el-icon-coordinate',
            buttonType: 'info',
            count: 1,
          },
          RESPONSE: {
            name: '响应回复',
            icon: 'el-icon-chat-line-square',
            buttonType: 'warning',
            count: 1,
          }
        },
        panel: {
          offsetLeft: -200,
          offsetTop: -200,
          zoom: 100,
          canMove: false
        },
        tempType: null,
        batchCount: 1,
      }
    },
    watch: {},
    mounted() {
      jsPlumb.ready(() => {
        this.jsPlumb = jsPlumb.getInstance()
        this.$nextTick(() => {
          setTimeout(() => {
            this.initInstance()
          }, 400)
        })
      })
      this.$nextTick(() => {
        this.sceneNodeDocument = document.getElementById('sceneNode')
      })
      // window.addEventListener('scroll', this.handleScroll, true)
    },
    destroyed() {
      // 离开该页面需要移除这个监听的事件，不然会报错
      // window.removeEventListener('scroll', this.handleScroll)
    },
    methods: {
      // 复制节点
      copySceneNode(sceneNode) {
        sceneNode = JSON.parse(JSON.stringify(sceneNode))
        sceneNode = {
          ...sceneNode,
          uuid: uuid(),
          name: sceneNode.name + '-(复制)',
          top: sceneNode.top + 35,
          left: sceneNode.left + 35,
          branchList: []
        }
        this.addOrUpdate(sceneNode)
      },
      onDrop(e) {
        const nodeType = this.nodeType[this.tempType];
        const sceneNode = {
          name: nodeType.name + "(" + (nodeType.count++) + ")",
          uuid: uuid(),
          nodeType: this.tempType,
          top: e.layerY,
          left: e.layerX,
          branchList: []
        }
        this.$nextTick(() => {
          this.addOrUpdate(sceneNode)
        })
      },
      onDragover(event) {
        event.preventDefault()
      },
      startDragOver(event, nodeType) {
        this.tempType = nodeType
      },
      addOrUpdate(sceneNode) {
        const index = this.sceneNodeList.findIndex((i) => i.uuid === sceneNode.uuid)
        if (index >= 0) {
          this.jsPlumb.reset()
          this.sceneNodeList.splice(index, 1, sceneNode)
          this.$nextTick(() => {
            this.initInstance()
          })
          this.$message({
            message: '修改成功',
            type: 'success'
          })
        } else {
          this.sceneNodeList.push(sceneNode)
          this.$nextTick(() => {
            this.initNode(sceneNode)
            this.initNodeConnect(sceneNode)
          })
          this.$message({
            message: '添加成功',
            type: 'success'
          })
        }
        this.nodeDrawerModel = false
      },
      openAddIntention() {
        this.$refs.SceneIntention.openAdd()
      },
      openAddSlot() {
        this.$refs.SceneSlot.openAdd()
      },
      viewSceneNode(sceneNode) {

      },
      // 删除节点
      removeSceneNode(sceneNode) {
        this.$confirm('是否确认删除节点[ ' + sceneNode.name + ' ]删除后该节点涉及的连接都会被删除', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          this.$message({
            message: '删除成功',
            type: 'success'
          })
          this.deleteNode(sceneNode)
        }).catch(() => {
        })
      },
      createFlow() {
        this.jsPlumb = jsPlumb.getInstance()
        this.$nextTick(() => {
          this.initInstance()
        })
      },
      addBatch(sceneNode) {
        const batch = {
          id: uuid(),
          name: "分支" + (this.batchCount++),
          nextNodeUuid: "",
        };
        sceneNode.branchList.push(batch)
        this.$nextTick(() => {
          this.jsPlumb.reset()
          this.$nextTick(() => {
            this.initInstance()
          })
        })
      },
      initInstance() {
        this.jsPlumb.batch(() => {
          for (const sceneNode of this.sceneNodeList) {
            this.initNode(sceneNode) // 初始化端点
          }
          for (const sceneNode of this.sceneNodeList) {
            this.initNodeConnect(sceneNode) // 初始化链接线
          }
        })
        // 双击连接线---删除连接线
        this.jsPlumb.bind('click', (connection) => {
          this.$confirm('确认要删除该分支连线吗?', '提示', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          }).then(() => {
            for (const sceneNode of this.sceneNodeList) {
              if (!sceneNode.branchList) continue
              for (const branch of sceneNode.branchList) {
                if ('branch_' + branch.id === connection.sourceId) {
                  branch.nextNodeUuid = ''
                  break
                }
              }
            }
            this.jsPlumb.deleteConnection(connection)
          }).catch(() => {
          })
        })
        // 手动连接
        this.jsPlumb.bind('connection', (connection) => {
          for (const sceneNode of this.sceneNodeList) {
            if (!sceneNode.branchList) continue
            const branch = sceneNode.branchList.find(i => 'branch_' + i.id === connection.sourceId)
            if (!branch) continue
            if (sceneNode.uuid === connection.targetId) {// 自己不能连接自己
              this.jsPlumb.deleteConnection(connection.connection)
              return
            }
            if (branch.nextNodeUuid === connection.targetId) {
              this.jsPlumb.deleteConnection(connection.connection)
              return
            }
            branch.nextNodeUuid = connection.targetId
            break
          }
        })
        // jsPlumb.fire('jsPlumbDemoLoaded', this.jsPlumb)
      },
      initNode(sceneNode) {
        // 节点上端可被连接
        if (sceneNode.nodeType !== 'ENTRANCE') {
          this.jsPlumb.makeTarget(sceneNode.uuid, {
            endpoint: 'Blank',
            anchor: 'Top'
          })
        }
        // 支持节点移动
        const stop = (el) => {
          const sceneNode = this.sceneNodeList.find((i) => i.uuid === el.el.id)
          if (sceneNode) {
            if (sceneNode.left !== el.pos[0] || sceneNode.top !== el.pos[1]) {
              sceneNode.left = el.pos[0]
              sceneNode.top = el.pos[1]
            }
          }
        }
        this.jsPlumb.draggable(sceneNode.uuid, {
          containment: 'parent',
          stop
        })
      },
      initNodeConnect(sceneNode) {
        if (sceneNode.branchList) {
          sceneNode.branchList.map(branch => {
            this.init(branch)
          })
        }
      },
      init(branch) {
        this.jsPlumb.makeSource('branch_' + branch.id, {
          endpoint: 'Blank',
          anchors: ['Bottom', 'Top'],
          maxConnections: 1,
          connectionsDetachable: false,
          connector: ['Bezier', {curviness: 63}],
          connectorStyle: {stroke: '#ccc', strokeWidth: 3},
          connectorHoverStyle: {stroke: '#1890ff', strokeWidth: 4},
          connectorOverlays: [['Arrow', {width: 10, location: 1, length: 12}]],
          connectionOverlays: [
            ['Label', {label: '', cssClass: '', labelStyle: {color: 'red'}}]
          ]
        })
        if (branch.nextNodeUuid && branch.nextNodeUuid !== '') {
          this.connectLine('branch_' + branch.id, branch.nextNodeUuid)
        }
      },
      deleteNode(sceneNode) {
        // 删除节点连线
        this.jsPlumb.deleteConnectionsForElement(sceneNode.uuid)

        // 删除节点分支连线
        if (sceneNode.branchList) {
          for (const branch of sceneNode.branchList) {
            this.jsPlumb.deleteConnectionsForElement('branch_' + branch.id)
          }
        }
        // 在数据中删除连接关系
        for (const sceneNode of this.sceneNodeList) {
          if (sceneNode.branchList) {
            for (const branch of sceneNode.branchList) {
              if (branch.nextNodeUuid === sceneNode.uuid) {
                branch.nextNodeUuid = ''
              }
            }
          }
        }
        // 删除节点
        for (const index in this.sceneNodeList) {
          if (this.sceneNodeList[index].uuid === sceneNode.uuid) {
            this.sceneNodeList.splice(index, 1)
            break
          }
        }
      },
      connectLine(sourceId, targetId) {
        this.jsPlumb.connect({
          endpoints: ['Blank', 'Blank'],
          source: sourceId,
          target: targetId,
          // scope: 'multi',
          anchors: ['Bottom', 'Top'],
          connectionsDetachable: false,
          connector: ['Bezier', {curviness: 63}],
          paintStyle: {stroke: '#ccc', strokeStyle: 3},
          hoverPaintStyle: {stroke: '#1890ff', strokeWidth: 4},
          overlays: [['Arrow', {width: 10, location: 1, length: 12}]],
          connectionOverlays: [
            ['Label', {label: '', cssClass: '', labelStyle: {color: 'red'}}]
          ]
        })
      },

      mousedownView(event) {
        this.panel.canMove = true
        this.initPageX = event.pageX
        this.initPageY = event.pageY
        this.panel.oldMarginLeft = this.panel.offsetLeft
        this.panel.oldMarginTop = this.panel.offsetTop
        on(document, 'mousemove', this.mousemoveView)
        on(document, 'mouseup', this.mouseupView)
      },
      mousemoveView(event) {
        if (!this.panel.canMove) return
        const {pageX, pageY} = event
        let offsetLeft = this.panel.oldMarginLeft + pageX - this.initPageX
        let width = this.sceneNodeDocument.clientWidth
        if (offsetLeft > 0) offsetLeft = 0
        if (3000 + offsetLeft < width) offsetLeft = width - 3000
        let height = this.sceneNodeDocument.clientHeight

        let offsetTop = this.panel.oldMarginTop + pageY - this.initPageY
        if (offsetTop > 0) offsetTop = 0
        if (1100 + offsetTop < height) offsetTop = height - 1100

        this.panel.offsetLeft = offsetLeft
        this.panel.offsetTop = offsetTop
      },
      mouseupView() {
        this.panel.canMove = false
        off(document, 'mousemove', this.mousemoveView)
        off(document, 'mouseup', this.mouseupView)
      },
      handleScroll() {
        var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
        console.log(scrollTop)
      }
    },
    computed: {
      orgTreeStyle() {
        return {
          transform: `translate(-50%, -50%) scale(${this.zoomHandled}, ${
            this.zoomHandled
          })`,
          marginLeft: `${this.panel.offsetLeft}px`,
          marginTop: `${this.panel.offsetTop}px`
        }
      },
      zoomHandled() {
        return this.zoom / 100
      }
    }
  }

</script>

<style>

    .panel-body {
        position: relative;
        background-color: #f5f5f5;
        width: 3000px;
        height: 2000px;
        background-image: radial-gradient(#a3b1bf 8%, transparent 0);
        background-size: 20px 20px;
        overflow: hidden;
    }

    .point {
        position: absolute;
        transition: 0s;
        border-radius: 4px;
        border: 1px solid #d7dae2;
        padding: 5px;
        min-width: 100px;
        background-color: #f5f7fa;
        /* mintcream  floralwhite */
    }

    path, ._jsPlumb_endpoint {
        cursor: pointer;
    }

    .addNodeClass {
        margin-top: 5px;
        margin-left: 50px;
        float: left;
        position: absolute;
        z-index: 25
    }

    .drawerStyles {
        overflow: auto;
    }

    .container {
        margin: 0 auto;
        width: 100%;
        height: 100%;
        max-height: 100px;
        /*overflow:hidden*/
    }

    #sceneNode .ivu-card-body {
        height: 100%;
        padding: 5px;
    }

    .node-drawer-footer {
        width: 700px;
        height: 60px;
        position: fixed;
        bottom: 0;
        right: 0;
        z-index: 101;
        border-top: 1px solid #e8e8e8;
        padding: 10px 16px;
        text-align: right;
        background: #fff;
    }
</style>
